<?php
/*
 * 公用方法文件 请按格式新增方法
 *
 * @author zxcvdavid@gmail.com
 *
 * exp:
 * if( ! function_exists('exp') ){
 *  function exp(){
 *
 *  }
 * }
 *
 */

namespace Common;

use Hashids\Hashids;
use Proto\Common\ReturnState;
use Yaf\Exception;
use Yaf\Registry;
use Firebase\JWT\JWT;

class Util{


    /**
     * emoji 2 json string
     * @param null $nickname
     * @return mixed|string
     */
    static public function emojiTextEncode($nickname = null){
        $str = is_string($nickname) && $nickname !== null ? trim($nickname) : '';
        if(empty($str)) return $nickname == null ? '' : $str;

        $text = json_encode($str); //暴露出unicode
        $text = preg_replace_callback("/(\\\u[ed][0-9a-f]{3})/i",function($str){
            return addslashes($str[0]);
        },$text); //将emoji的unicode留下，其他不动，这里的正则比原答案增加了d，因为我发现我很多emoji实际上是\ud开头的，反而暂时没发现有\ue开头。
        return json_decode($text);
    }

    /**
     * json string 2 emoji
     * @param null $decode_nickname
     * @return mixed
     */
    static public function emojiTextDecode($decode_nickname = null){
        $text = json_encode($decode_nickname); //暴露出unicode
        $text = preg_replace_callback('/\\\\\\\\/i',function($str){
            return '\\';
        },$text); //将两条斜杠变成一条，其他不动
        return json_decode($text);

    }

    static public function getRandStr($type = 'num', $length = 6){
        $type = !empty($type) && in_array($type, ['num', 'letter', 'mix']) ? $type : 'num';
        $length = !empty($length) && is_numeric($length) ? $length : 6;

        $str = null;
        if($type == 'mix'){
            $type = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(){}[].,<>\'":';
        }else if($type == 'str'){
            $type = 'abcdefghijklmnopqrstuvwxyz';
        }else if($type == 'num'){
            $type = '0123456789';
        }
        $max = strlen($type)-1;

        $return = '';
        for($i=0; $i<$length; $i++){
            $return .= $type[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }

        return $return;
    }

    static public function getIP(){
        if (isset($_SERVER)) {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $real_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $real_ip = $_SERVER['HTTP_CLIENT_IP'];
            } else {
                $real_ip = $_SERVER['REMOTE_ADDR'];
            }
        } else {
            if (getenv("HTTP_X_FORWARDED_FOR")) {
                $real_ip = getenv( "HTTP_X_FORWARDED_FOR");
            } elseif (getenv("HTTP_CLIENT_IP")) {
                $real_ip = getenv("HTTP_CLIENT_IP");
            } else {
                $real_ip = getenv("REMOTE_ADDR");
            }
        }
        return $real_ip;
    }

    static public function remove_invisible_characters($str, $url_encoded = TRUE)
    {
        $non_displayables = array();

        // every control character except newline (dec 10),
        // carriage return (dec 13) and horizontal tab (dec 09)
        if ($url_encoded)
        {
            $non_displayables[] = '/%0[0-8bcef]/';	// url encoded 00-08, 11, 12, 14, 15
            $non_displayables[] = '/%1[0-9a-f]/';	// url encoded 16-31
        }

        $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127

        do
        {
            $str = preg_replace($non_displayables, '', $str, -1, $count);
        }
        while ($count);

        return $str;
    }

    static public function object2array($obj){
        foreach ($obj as $k => $v){
            $arr[$k] = $v;
            if(gettype($v) == 'object' || gettype($v) == 'array'){
                $arr[$k] = (array)Util::object2array($v);
            }
        }
        return $arr;
    }

    static public function encrypt($data, $key)
    {
        $key = md5($key);
        $x = 0;
        $len = strlen($data);
        $l = strlen($key);
        $str = $char = '';
        for ($i = 0; $i < $len; $i++)
        {
            if ($x == $l)
            {
                $x = 0;
            }
            $char .= $key{$x};
            $x++;
        }
        for ($i = 0; $i < $len; $i++)
        {
            $str .= chr(ord($data{$i}) + (ord($char{$i})) % 256);
        }
        return base64_encode($str);
    }

    static public function decrypt($data, $key)
    {
        $key = md5($key);
        $x = 0;
        $data = base64_decode($data);
        $len = strlen($data);
        $l = strlen($key);
        $str = $char = '';
        for ($i = 0; $i < $len; $i++)
        {
            if ($x == $l)
            {
                $x = 0;
            }
            $char .= substr($key, $x, 1);
            $x++;
        }
        for ($i = 0; $i < $len; $i++)
        {
            if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1)))
            {
                $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
            }
            else
            {
                $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
            }
        }
        return $str;
    }

    static public function curlRequest($url, $data = [], $method = 'GET', $use_cert = false, $second = 30){
        $key = Registry::get("config")["api"]["md5"]["access_key"];
        $token = [
            'jti' => '37c107e4609ddbcc9c096ea5ee76c667',
            'iss' => 'https://api.37egou.com/',
            'iat' => time(),
            'exp' => time()+10,
        ];
        JWT::$leeway = 10;
        $jwt = JWT::encode($token, $key);

        $ch = curl_init();
        //设置超时与地址
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);

        if(strpos($url, 'https://') !== false){
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验
        }

        //设置header
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-HTTP-Method-Override: $method"]);
        curl_setopt($ch, CURLOPT_USERPWD, $jwt);

        if($use_cert === true){
            //设置证书
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, Registry::get("config")['slcert_path']);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, Registry::get("config")['sslkey_pem']);
        }

        if(strtoupper($method) === 'POST'){
            curl_setopt($ch, CURLOPT_PORT, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        $document = curl_exec($ch);
        /*if(curl_errno($ch)){
            return new \Exception('接口调用失败' . curl_error($ch), '999');
        }*/
        curl_close($ch);
        return $document;
    }

    static public function mobileIsValid($mobile = ''){
        $mobile = !empty($mobile) && is_string($mobile) ? trim($mobile) : '';
        if(empty($mobile))
            return new Exception("参数不能为空", "001");

        //判断手机号码长度
        if(strlen($mobile) < 11)
            return new Exception("请输入正确手机号码", "002");

        //正则手机号码是否有效
        if(!preg_match("/^13[0-9]{1}[0-9]{8}$|15[01289]{1}[0-9]{8}$|18[0-9]{9}|177[0-9]{8}$/", $mobile))
            return new Exception("请输入正确的手机号码", "003");

        return true;
    }

    /**
     * 生成订单编号
     * @return string
     */
    static public function genOrderSn(){
        $y_code = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        return $y_code[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
    }

    /**
     * 格式化单位
     */
    static public function byteFormat( $size, $dec = 2 ) {
        $a = array ( "B" , "KB" , "MB" , "GB" , "TB" , "PB" );
        $pos = 0;
        while ( $size >= 1024 ) {
            $size /= 1024;
            $pos ++;
        }
        return round( $size, $dec ) . " " . $a[$pos];
    }

    /**
     * 生成uuid
     * @param string $identifier_key
     * @return string
     */
    static public function genUuid($identifier_key = ''){
        $hash_id = new Hashids('gamehome', 16, 'abcdefghijklmnopqrstuvwxyz01234569');

        return $hash_id->encode($identifier_key);
        /*$s = uniqid('', $more_entropy);
        if (!$more_entropy)
            return base_convert($s, 16, 36);

        $hex = substr($s, 0, 13);
        $dec = $s[13] . substr($s, 15); // skip the dot
        return base_convert($hex, 16, 36) . base_convert($dec, 10, 36);*/
    }

    /**
     * 格式化字符串
     * @param string $str
     * @return string
     */
    static public function formatStr($str) {
        $arr = array(' ', ' ', '&', '@', '#', '%',  '\'', '"', '\\', '/', '.', ',', '$', '^', '*', '(', ')', '[', ']', '{', '}', '|', '~', '`', '?', '!', ';', ':', '-', '_', '+', '=');
        foreach ($arr as $v) {
            $str = str_replace($v, '', $str);
        }
        return $str;
    }

    /**
     * 获得真实IP地址
     * @return string
     */
    static public function realIp() {
        static $realip = NULL;
        if ($realip !== NULL) return $realip;
        if (isset($_SERVER)) {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
                foreach ($arr AS $ip) {
                    $ip = trim($ip);
                    if ($ip != 'unknown') {
                        $realip = $ip;
                        break;
                    }
                }
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $realip = $_SERVER['HTTP_CLIENT_IP'];
            } else {
                if (isset($_SERVER['REMOTE_ADDR'])) {
                    $realip = $_SERVER['REMOTE_ADDR'];
                } else {
                    $realip = '0.0.0.0';
                }
            }
        } else {
            if (getenv('HTTP_X_FORWARDED_FOR')) {
                $realip = getenv('HTTP_X_FORWARDED_FOR');
            } elseif (getenv('HTTP_CLIENT_IP')) {
                $realip = getenv('HTTP_CLIENT_IP');
            } else {
                $realip = getenv('REMOTE_ADDR');
            }
        }
        preg_match('/[\d\.]{7,15}/', $realip, $onlineip);
        $realip = !empty($onlineip[0]) ? $onlineip[0] : '0.0.0.0';
        return $realip;
    }

    /**
     * 创建目录
     * @param string $dir
     */
    static public function createDir($dir) {
        if (!is_dir($dir)) {
            mkdir($dir, 0777);
        }
    }

    /**
     * 删除目录
     * @param string $path
     */
    static public function delDir($path) {
        if (is_dir($path)) rmdir($path);
    }

    /**
     * 创建文件（默认为空）
     * @param unknown_type $filename
     */
    static public function createFile($filename) {
        if (!is_file($filename)) touch($filename);
    }

    /**
     * 删除文件
     * @param string $filename
     */
    static public function delFile($filename) {
        if (file_exists($filename)) unlink($filename);
    }

    /**
     * 删除目录及地下的全部文件
     * @param string $dir
     * @return bool
     */
    static public function delDirOfAll($dir) {
    //先删除目录下的文件：
        if (is_dir($dir)) {
            $dh=opendir($dir);
            while (!!$file=readdir($dh)) {
                if($file!="." && $file!="..") {
                    $fullpath=$dir."/".$file;
                    if(!is_dir($fullpath)) {
                        unlink($fullpath);
                    } else {
                        self::delDirOfAll($fullpath);
                    }
                }
            }
            closedir($dh);
    //删除当前文件夹：
            if(rmdir($dir)) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 给已经存在的图片添加水印
     * @param string $file_path
     * @return bool
     */
    static public function addMark($file_path) {
        if (file_exists($file_path) && file_exists(MARK)) {
    //求出上传图片的名称后缀
            $ext_name = strtolower(substr($file_path, strrpos($file_path, '.'), strlen($file_path)));
    //$new_name='jzy_' . time() . rand(1000,9999) . $ext_name ;
            $store_path = ROOT_PATH . UPDIR;
    //求上传图片高宽
            $imginfo = getimagesize($file_path);
            $width = $imginfo[0];
            $height = $imginfo[1];
    //添加图片水印
            switch($ext_name) {
                case '.gif':
                    $dst_im = imagecreatefromgif($file_path);
                    break;
                case '.jpg':
                    $dst_im = imagecreatefromjpeg($file_path);
                    break;
                case '.png':
                    $dst_im = imagecreatefrompng($file_path);
                    break;
            }
            $src_im = imagecreatefrompng(MARK);
    //求水印图片高宽
            $src_imginfo = getimagesize(MARK);
            $src_width = $src_imginfo[0];
            $src_height = $src_imginfo[1];
    //求出水印图片的实际生成位置
            $src_x = $width - $src_width - 10;
            $src_y = $height - $src_height - 10;
    //新建一个真彩色图像
            $nimage = imagecreatetruecolor($width, $height);
    //拷贝上传图片到真彩图像
            imagecopy($nimage, $dst_im, 0, 0, 0, 0, $width, $height);
    //按坐标位置拷贝水印图片到真彩图像上
            imagecopy($nimage, $src_im, $src_x, $src_y, 0, 0, $src_width, $src_height);
    //分情况输出生成后的水印图片
            switch($ext_name) {
                case '.gif':
                    imagegif($nimage, $file_path);
                    break;
                case '.jpg':
                    imagejpeg($nimage, $file_path);
                    break;
                case '.png':
                    imagepng($nimage, $file_path);
                    break;
            }
    //释放资源
            imagedestroy($dst_im);
            imagedestroy($src_im);
            unset($imginfo);
            unset($src_imginfo);
    //移动生成后的图片
            @move_uploaded_file($file_path, ROOT_PATH.UPDIR . $file_path);
        }
    }

    static $return_arr = [];

    /**
     * 尾递归将传入的数组变成二维数组
     * @param array $recursion_arr
     * @param string $type
     * @param int $level
     */
    static public function tailRecursion($recursion_arr = [], $type = '', $level = 0){
        if(!is_array($recursion_arr['value'])){
            $recursion_arr['value'] = strpos($recursion_arr['value'], ',') !== false ?
                explode(',', $recursion_arr['value']) : [$recursion_arr['value']];
        }
        if(!is_array($recursion_arr['type'])){
            $recursion_arr['type'] = strpos($recursion_arr['type'], ',') !== false ?
                explode(',', $recursion_arr['type']) : [$recursion_arr['type']];
        }

        foreach ($recursion_arr['value'] as $key => $value){
            if($type){
                if(!isset(self::$return_arr[$type][$value])){
                    self::$return_arr[$type][$value] = $recursion_arr['type'][$key];
                }
            }else{
                // 以变量名为键，对应类型为值
                self::$return_arr[$value] = $recursion_arr['type'][$key];
            }

            if(!in_array($recursion_arr['type'][$key], ['int', 'bool', 'string', 'enum'])){
                if(isset(Registry::get('config')['api']['message'][$recursion_arr['type'][$key]])){
                    $new_recursion_arr = Registry::get('config')['api']['message'][$recursion_arr['type'][$key]];
                    $level += 1;
                    self::tailRecursion($new_recursion_arr, $recursion_arr['type'][$key], $level);
                }
            }
        }
    }

    static $map_obj_arr = [];
    static $proto_file_name = '';

    /**
     * 将二维数组中对应的类型与方法生成好，实例化类并调用对应的set方法，将数组里的值set进去
     * @param $proto_class
     * @param $parameters_arr
     * @param string $parent
     * @param string $parent_proto_class
     * @param string $parent_proto_set_method
     * @return mixed
     */
    static $map_len = 0;
    static $max_count_arr = [];
    static $map_arr = [];

    static public function protoResponse($proto_class, $parameters_arr, $parent='', $parent_proto_class = '', $parent_proto_set_method = ''){
        Registry::get('proto_log')->err('----------------------------------------------------');
        $par = [
            is_object($proto_class) ? get_class($proto_class) : '',
            $parameters_arr,
            $parent,
            is_object($parent_proto_class) ? get_class($parent_proto_class) : '',
            $parent_proto_set_method
        ];
        Registry::get('proto_log')->err(json_encode($par));
//        Registry::get('proto_log')->err('$parameters_arr: ' . json_encode($parameters_arr));
//        Registry::get('proto_log')->err('$proto_class: ' . json_encode(is_object($proto_class) ? get_class($proto_class) : $proto_class));
//        exit(json_encode(self::$return_arr));
        foreach ($parameters_arr as $item => $value) {
            // 当传入的parameters_arr是一个以数字为键的数组时，表示要使用map
            // 需要写入map_obj_arr，最后出去的时候再set到arr结尾的变量名的map中
            if(is_integer($item)){
                Registry::get('proto_log')->notice('method:' . $parent_proto_set_method);
                Registry::get('proto_log')->notice(json_encode($value));
                // map是一个对象
                $return_proto_property_arr = [];
                if(is_object($proto_class)){
                    if(count($parameters_arr) == 1){
                        $value = self::protoResponse($proto_class, $value);
                    }else{
                        $proto_class = new $proto_class();
                        $value = self::protoResponse($proto_class, $value);
                    }
//                    $value = self::protoResponse($proto_class, $value);
                    $return_proto_property_arr = array_keys(self::$return_arr, $parent);
                }
                // map是一个<int, int/bool/string>
                else{
                    // 上级proto对象的方法存在
                    if(in_array($parent_proto_set_method, get_class_methods($parent_proto_class))){
                        $return_proto_property_arr = [substr($parent_proto_set_method, 3)];
                    }
                }
                Registry::get('proto_log')->notice('return_proto_property_arr: ' . json_encode($return_proto_property_arr));

                if($return_proto_property_arr){
                    $return_proto_property = $return_proto_property_arr[0];
                    if(strpos($return_proto_property, 'Arr')){
                        Registry::get('proto_log')->warn(json_encode(func_get_args()));
                        self::$map_arr[$parent_proto_set_method][is_numeric($item) ? (int)$item : $item] =
                            is_numeric($value) ? (int)$value : $value;
                        self::$max_count_arr[$parent_proto_set_method]++;
                        Registry::get('proto_log')->warn('current len: ' . self::$max_count_arr[$parent_proto_set_method] . ' total: ' . count($parameters_arr));


                        Registry::get('proto_log')->notice(json_encode(self::$map_arr));
                        if(count($parameters_arr) == self::$max_count_arr[$parent_proto_set_method]){
                            Registry::get('proto_log')->err(json_encode([is_array(self::$map_arr[$parent_proto_set_method]), $parent_proto_set_method]));

                            $parent_proto_class->$parent_proto_set_method(self::$map_arr[$parent_proto_set_method]);
                            self::$map_arr[$parent_proto_set_method] = [];
                            self::$max_count_arr[$parent_proto_set_method] = 0;
                        }
                    }else{
                        $parent_proto_class->$parent_proto_set_method($value);
                    }
                }
            }
            // 非数字键
            else{
                // 根据键，生成要调用的方法名，若键名以arr结尾，且value是一个多维数组时，表示这是一个map
                $property_set_method_name = 'set' . ucfirst($item);
                Registry::get('proto_log')->err(json_encode('$property_set_method_name:' . $property_set_method_name));
                // 存在上级proto对象，既本次调用为第n次调用，n>1
                if($parent){
                    // 对应值为数组，表示需要寻找对应的下级proto对象
                    if(is_array($value)){
                        // 判断是否存在下级是否存在
                        if(isset(self::$return_arr[$parent][$item])){
                            // 下级若是proto对象，则实例化对象，则直接递归
                            if(!in_array(self::$return_arr[$parent][$item], ['int', 'string', 'bool', 'enum'])){
                                $sub_proto_class_name = '\\Proto\\'.self::$proto_file_name.'\\' . self::$return_arr[$parent][$item];
                                $sub_proto = new $sub_proto_class_name();
                                $value = self::protoResponse($sub_proto, $value, self::$return_arr[$parent][$item]);
                            }
                            // 下级不是一个proto对象若存在则递归
                            else{
                                $sub_proto = '';
                                self::$map_len = self::arrayDepth($value);
                                self::$max_count_arr[$property_set_method_name] = 0;
                                // 根据value的维度判断是不是map，维度==1为对象，维度>1为map
                                // 当维度==1时，需要将其改造为一个二维数组，给定上级proto对象与上级proto对象要调用的方法名
                                Registry::get('proto_log')->info('depth: ' . self::arrayDepth($value) . 'value: ' . json_encode($value));
                                Registry::get('proto_log')->info('method_name: ' . $property_set_method_name);
                                Registry::get('proto_log')->warn('max_count: ' . self::$max_count_arr[$property_set_method_name]);
                                if(is_object($sub_proto)){
                                    var_dump(get_class($sub_proto));
                                }else{
                                    Registry::get('proto_log')->warn('sub_proto: ' . $sub_proto);
                                }
                                $value = self::protoResponse(
                                    $sub_proto,
                                    is_object($sub_proto) ? (self::arrayDepth($value) == 1 ? [$value] : $value) : $value,
                                    isset(self::$return_arr[$item]) ? self::$return_arr[$item] : '',
                                    $proto_class,
                                    $property_set_method_name
                                );
//                                else{
//                                    $sub_proto->$property_set_method_name($value);
//                                }
                            }
                        }
                    }else{
                        // 对应值不为数组且不为空，则表示需要将该值赋值给对应proto类
                        if($value) {
//                            $value = self::protoResponse($proto_class, $value, $parent);
                        }
                    }

                }
                // 不存在上级proto对象，既本次调用为第一次调用
                else{
                    Registry::get('proto_log')->info('$value:' . json_encode($value));
                    if(is_array($value)){
                        // value是一个message
                        if(isset(self::$return_arr[$item]) && !in_array(self::$return_arr[$item], ['int', 'string', 'bool', 'enum'])){
                            $sub_proto_class_name = '\\Proto\\'.self::$proto_file_name.'\\' . self::$return_arr[$item];
                            $sub_proto = new $sub_proto_class_name();
                        }
                        // value是一个数组map<int, int/bool/string>
                        else{
                            $sub_proto = '';
                        }
                        // 当值不为空数组时，表示需要调用对应message的类或需要给map遍历赋值，故调用准备参数，进行递归
                        if($value){
                            self::$map_len = self::arrayDepth($value);
                            self::$max_count_arr[$property_set_method_name] = 0;
                            // 根据value的维度判断是不是map，维度==1为对象，维度>1为map
                            // 当维度==1时，需要将其改造为一个二维数组，给定上级proto对象与上级proto对象要调用的方法名
                            Registry::get('proto_log')->info('depth: ' . self::arrayDepth($value) . 'value: ' . json_encode($value));
                            Registry::get('proto_log')->info('method_name: ' . $property_set_method_name);
                            Registry::get('proto_log')->warn('max_count: ' . self::$max_count_arr[$property_set_method_name]);
//                            Registry::get('proto_log')->warn('sub_proto: ' . is_string($sub_proto) ? $sub_proto : get_class($sub_proto));
                            if(is_object($sub_proto)){
                                $value = self::protoResponse(
                                    $sub_proto,
                                    is_object($sub_proto) ? (self::arrayDepth($value) == 1 ? [$value] : $value) : $value,
                                    isset(self::$return_arr[$item]) ? self::$return_arr[$item] : '',
                                    $proto_class,
                                    $property_set_method_name
                                );
                            }else{
//                                var_dump(get_class($proto_class));
//                                var_dump($value);;
//                                var_dump($property_set_method_name);
//                                var_dump('--------------');
                                $proto_class->$property_set_method_name($value);
                            }
                        }
                    }
                }


                if(strrpos($item, 'Arr') === false || strrpos($item, 'Arr') + strlen('Arr') != strlen($item)){
//                    Registry::get('proto_log')->notice('proto1:' . get_class($proto_class));
                    Registry::get('proto_log')->notice('item1:' . $item);
                    Registry::get('proto_log')->notice('method1:' . $property_set_method_name);
                    Registry::get('proto_log')->notice('value1:' . is_array($value) ? json_encode($value) : $value);
                    $proto_class->$property_set_method_name($value);
                }
            }
        }

        return $proto_class;
    }

    /**
     * 根据接口所在的controller与action在配置文件中查找，对应的返回协议名称
     * @param string $class_name
     * @param string $func_name
     * @return array
     * @throws Exception
     */
    static public function getParam4Proto($class_name = '', $func_name = ''){
        if(empty($class_name) || empty($func_name))
            throw new Exception('无效参数!!!', ReturnState::qg_port_error);

        $return_proto = Registry::get('config')['api'][$class_name]['message'][$func_name];
        // 返回协议 == 请求协议+Back
        if(is_array($return_proto)){
            $parameters = Registry::get("config")['api'][$class_name]['message'][$func_name];
        }
        // 该情况属于当返回协议不以请求协议+Back为名称的时候使用
        else{
            $parameters = isset(Registry::get('config')['api'][$class_name]['message'][$return_proto]) ?
                Registry::get('config')['api'][$class_name]['message'][$return_proto] : [];
            $func_name = $return_proto;
        }

        if($parameters){
            return [$func_name, $parameters];
        }else{
            throw new Exception('无效参数!!', ReturnState::qg_port_error);
        }
    }

    /**
     * 判断数组维度
     * @param $array
     * @return int
     * @throws Exception
     */
    public static function arrayDepth($array) {
        if(!is_array($array))
            throw new Exception('这不是一个数组,无法判断维度', ReturnState::qg_port_error);
        $max_depth = 1;
        foreach ($array as $value) {
            if (is_array($value) && !empty($value)) {
                $depth = self::arrayDepth($value) + 1;

                if ($depth > $max_depth) {
                    $max_depth = $depth;
                }
            }
        }
        return $max_depth;
    }

    public static function calculationWeight($weight_array = [], $weight = 'scale', $return_column = 'id'){
        if(empty($weight_array))
            return $weight_array;

        $scale_total = 0;
        foreach($weight_array as $key_total=> $value_total){
            $scale_total += $value_total[$weight];
        }
        $random = mt_rand(1, $scale_total);
        $min = 0;
        $max = 0;
        $item = [];
        foreach ($weight_array as $k => $value){
            $max += $value[$weight];
            $min = ($k == 0 ? 0 : $weight_array[$k - 1][$weight]) + $min;
            if($random > $min && $random <= $max){
                $item = $value[$return_column];
                break;
            }
        }

        return $item;
    }

    static public function dateString2timestamp($date_time_str = ''){
        if (empty($date_time_str))
            return '';

        $year = substr($date_time_str, 0, 4);
        $mouth = substr($date_time_str, 4, 2) < '12' ? substr($date_time_str, 4, 2) : '12';
        $day = substr($date_time_str, 6, 2) < '31' ? substr($date_time_str, 6, 2) : '31';
        $hour = substr($date_time_str, 8, 2) > '24' ? substr($date_time_str, 8, 2) : '24';
        $minute = substr($date_time_str, 10, 2) < '59' ? substr($date_time_str, 10, 2) : '59';
        $second = substr($date_time_str, 12, 2) < '59' ? substr($date_time_str, 12, 2) : '59';

        $date_time_string = str_replace(
            ['#year#', '#mouth#', '#day#', '#hour#', '#minute#', '#second#'],
            [$year, $mouth, $day, $hour, $minute, $second],
            '#year#-#mouth#-#day# #hour#:#minute#:#second#');

        return strtotime($date_time_string);
    }
}


